package com.yc.cloud.admin.service.impl;

import cn.hutool.core.date.DateUtil;
import com.yc.cloud.admin.config.AlarmEmailProperties;
import com.yc.cloud.admin.service.AlarmEmailService;
import com.yc.cloud.api.constants.RedisKeyConstants;
import com.yc.cloud.common.cache.service.RedisService;
import com.yc.cloud.common.oss.service.OssFileService;
import com.yc.cloud.entity.AlarmRecord;
import com.yc.cloud.entity.AlgAlgorithm;
import com.yc.cloud.entity.HwCamera;
import com.yc.cloud.entity.HwDevice;
import com.yc.cloud.entity.UmsAdmin;
import com.yc.cloud.mapper.AlgAlgorithmMapper;
import com.yc.cloud.mapper.HwCameraMapper;
import com.yc.cloud.mapper.HwDeviceMapper;
import com.yc.cloud.mapper.UmsAdminMapper;
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

/**
 * 告警邮件服务实现类
 *
 * @author JackGu
 * @since 2025-11-07
 */
@Slf4j
@Service
public class AlarmEmailServiceImpl implements AlarmEmailService {

    @Resource
    private JavaMailSender mailSender;

    @Resource
    private TemplateEngine templateEngine;

    @Resource
    private AlarmEmailProperties alarmEmailProperties;

    @Resource
    private AlgAlgorithmMapper algAlgorithmMapper;

    @Resource
    private HwDeviceMapper hwDeviceMapper;

    @Resource
    private HwCameraMapper hwCameraMapper;

    @Resource
    private UmsAdminMapper umsAdminMapper;

    @Resource
    private OssFileService ossFileService;

    @Resource
    private RedisService<Object> redisService;

    @Value("${spring.mail.username}")
    private String from;

    @Override
    public void sendAlarmEmail(AlarmRecord alarmRecord) {
        // 卫语句：检查是否启用邮件告警
        if (!alarmEmailProperties.getEnabled()) {
            log.debug("邮件告警功能未启用，跳过发送");
            return;
        }

        // 卫语句：检查是否需要发送邮件
        if (!shouldSendEmail(alarmRecord.getAlgorithmId())) {
            log.debug("告警类型不需要发送邮件: algorithmId={}", alarmRecord.getAlgorithmId());
            return;
        }

        // 卫语句：检查是否在发送间隔内（限流）
        if (isWithinSendInterval(alarmRecord.getAlgorithmId())) {
            log.info("同一算法类型的邮件发送过于频繁，已跳过: algorithmId={}, interval={}秒", 
                alarmRecord.getAlgorithmId(), alarmEmailProperties.getSendIntervalSeconds());
            return;
        }

        // 卫语句：检查是否有接收人
        if (alarmEmailProperties.getRecipientList().isEmpty()) {
            log.warn("未配置邮件接收人，无法发送告警邮件");
            return;
        }

        try {
            // 构建邮件内容
            String emailContent = buildEmailContent(alarmRecord);

            // 创建邮件消息
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

            // 设置发件人和收件人
            helper.setFrom(from);
            helper.setTo(alarmEmailProperties.getRecipientList().toArray(new String[0]));

            // 设置邮件主题和内容
            helper.setSubject(String.format("【紧急告警】%s - %s", 
                alarmRecord.getAlarmName(), 
                DateUtil.format(alarmRecord.getAlarmTime(), "yyyy-MM-dd HH:mm:ss")));
            helper.setText(emailContent, true);

            // 附加告警图片作为内嵌资源
            attachAlarmImage(helper, alarmRecord);

            // 发送邮件
            mailSender.send(message);
            log.info("告警邮件发送成功: alarmId={}, recipients={}", 
                alarmRecord.getId(), alarmEmailProperties.getRecipientList());

            // 记录发送时间到Redis（用于限流）
            recordEmailSent(alarmRecord.getAlgorithmId());

        } catch (MessagingException e) {
            log.error("发送告警邮件失败: alarmId={}", alarmRecord.getId(), e);
        } catch (Exception e) {
            log.error("发送告警邮件异常: alarmId={}", alarmRecord.getId(), e);
        }
    }

    @Async
    @Override
    public void sendAlarmEmailAsync(AlarmRecord alarmRecord) {
        sendAlarmEmail(alarmRecord);
    }

    @Override
    public boolean shouldSendEmail(Long algorithmId) {
        // 卫语句：检查是否启用邮件功能
        if (!alarmEmailProperties.getEnabled()) {
            return false;
        }

        // 卫语句：检查算法ID是否有效
        if (algorithmId == null) {
            log.warn("算法ID为空，无法判断是否发送邮件");
            return false;
        }

        // 从数据库查询算法配置
        AlgAlgorithm algorithm = algAlgorithmMapper.selectById(algorithmId);

        // 卫语句：检查算法是否存在
        if (algorithm == null) {
            log.warn("未找到算法信息: algorithmId={}", algorithmId);
            return false;
        }

        // 判断该算法是否启用邮件告警
        boolean enableEmail = Boolean.TRUE.equals(algorithm.getEnableEmail());
        
        log.debug("算法邮件告警配置: algorithmId={}, algorithmName={}, enableEmail={}", 
            algorithmId, algorithm.getName(), enableEmail);

        return enableEmail;
    }

    /**
     * 构建邮件内容
     */
    private String buildEmailContent(AlarmRecord alarmRecord) {
        Context context = new Context();

        // 基本告警信息
        context.setVariable("alarmName", alarmRecord.getAlarmName());
        context.setVariable("alarmTime", DateUtil.format(alarmRecord.getAlarmTime(), "yyyy-MM-dd HH:mm:ss"));
        context.setVariable("deviceLocation", alarmRecord.getDeviceLocation());

        // 获取设备信息
        if (alarmRecord.getDeviceId() != null) {
            HwDevice device = hwDeviceMapper.selectById(alarmRecord.getDeviceId());
            if (device != null) {
                context.setVariable("deviceName", device.getName());
                context.setVariable("deviceSn", device.getDeviceSn());
            }
        }

        // 获取摄像头信息
        if (alarmRecord.getCameraId() != null) {
            HwCamera camera = hwCameraMapper.selectById(alarmRecord.getCameraId());
            if (camera != null) {
                context.setVariable("cameraName", camera.getName());
                context.setVariable("cameraLocation", camera.getLocation());
            }
        }

        // 获取违规人员信息
        if (alarmRecord.getViolatorUserId() != null) {
            UmsAdmin user = umsAdminMapper.selectById(alarmRecord.getViolatorUserId());
            if (user != null) {
                context.setVariable("violatorName", 
                    user.getTrueName() != null ? user.getTrueName() : user.getUsername());
                if (alarmRecord.getFaceSimilarity() != null) {
                    context.setVariable("faceSimilarity", 
                        String.format("%.1f%%", alarmRecord.getFaceSimilarity() * 100));
                }
            }
        }

        // 获取告警图片URL（用于模板中显示）
        if (alarmRecord.getAlarmMarkedImageFileId() != null) {
            String imageUrl = ossFileService.getOssFileUrl(alarmRecord.getAlarmMarkedImageFileId());
            context.setVariable("imageUrl", imageUrl);
            // 标记图片是否存在（用于控制是否显示图片区域）
            context.setVariable("hasImage", true);
        } else {
            context.setVariable("hasImage", false);
        }

        // 使用模板引擎生成HTML内容
        return templateEngine.process("alarm-email", context);
    }

    /**
     * 附加告警图片作为内嵌资源
     */
    private void attachAlarmImage(MimeMessageHelper helper, AlarmRecord alarmRecord) {
        // 卫语句：检查是否有图片文件ID
        if (alarmRecord.getAlarmMarkedImageFileId() == null) {
            log.debug("告警记录没有图片: alarmId={}", alarmRecord.getId());
            return;
        }

        try {
            // 获取OSS文件对象
            com.yc.cloud.common.oss.entity.OssFile ossFile = 
                ossFileService.getByFileIdFromDb(alarmRecord.getAlarmMarkedImageFileId());
            
            // 卫语句：检查文件是否存在
            if (ossFile == null) {
                log.warn("OSS文件记录不存在: fileId={}", alarmRecord.getAlarmMarkedImageFileId());
                return;
            }

            // 获取文件路径
            String filePath = ossFile.getPath();
            File imageFile = new File(filePath);

            // 卫语句：检查物理文件是否存在
            if (!imageFile.exists()) {
                log.warn("告警图片文件不存在: path={}", filePath);
                return;
            }

            // 读取文件字节
            byte[] imageBytes = Files.readAllBytes(imageFile.toPath());

            // 将图片作为内嵌资源添加到邮件中（使用CID）
            helper.addInline("alarmImage", new ByteArrayResource(imageBytes), "image/jpeg");
            
            log.debug("告警图片已附加到邮件: alarmId={}, fileSize={} bytes", 
                alarmRecord.getId(), imageBytes.length);

        } catch (IOException e) {
            log.error("读取告警图片文件失败: fileId={}", alarmRecord.getAlarmMarkedImageFileId(), e);
        } catch (MessagingException e) {
            log.error("附加告警图片到邮件失败: fileId={}", alarmRecord.getAlarmMarkedImageFileId(), e);
        }
    }

    /**
     * 检查该算法是否在发送间隔内（限流检查）
     *
     * @param algorithmId 算法ID
     * @return true-在间隔内（需要限流），false-不在间隔内（可以发送）
     */
    private boolean isWithinSendInterval(Long algorithmId) {
        // 卫语句：检查算法ID是否有效
        if (algorithmId == null) {
            return false;
        }

        // 构建Redis Key
        String redisKey = RedisKeyConstants.ALARM_EMAIL_SENT
            .replace(RedisKeyConstants.ALGORITHM_ID, String.valueOf(algorithmId));

        // 检查Redis中是否存在该Key（存在则表示在限流时间内）
        Boolean exists = redisService.hasKey(redisKey);
        return Boolean.TRUE.equals(exists);
    }

    /**
     * 记录邮件发送时间到Redis（用于限流）
     *
     * @param algorithmId 算法ID
     */
    private void recordEmailSent(Long algorithmId) {
        // 卫语句：检查算法ID是否有效
        if (algorithmId == null) {
            return;
        }

        try {
            // 构建Redis Key
            String redisKey = RedisKeyConstants.ALARM_EMAIL_SENT
                .replace(RedisKeyConstants.ALGORITHM_ID, String.valueOf(algorithmId));

            // 获取发送间隔时间（秒）
            Long intervalSeconds = alarmEmailProperties.getSendIntervalSeconds();

            // 记录到Redis，设置过期时间为发送间隔
            redisService.set(redisKey, System.currentTimeMillis(), intervalSeconds);

            log.debug("记录邮件发送时间到Redis: algorithmId={}, interval={}秒, key={}", 
                algorithmId, intervalSeconds, redisKey);

        } catch (Exception e) {
            // 记录失败不影响邮件发送，只记录日志
            log.error("记录邮件发送时间到Redis失败: algorithmId={}", algorithmId, e);
        }
    }
}

